package juc.thread.secuity;

import java.sql.*;
import java.util.*;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicIntegerArray;

/*
 * 运用享元模式 DIY一个 数据库连接池（享元模式）
 *
 *
 * 后续Semaphore 因为数据库连接 一个线程对应一个连接 所以可以考虑用Semaphore（信号量）更加方便的来实现资源的共享与等待
 * */
public class Test17{
    public static void main(String[] args) {
        DatabasePool databasePool = new DatabasePool(1);
        for (int i = 0; i < 2; i++) {
            new Thread(() -> {
                try {
                    Connection connection = databasePool.borrow();
                    Thread.sleep(9000);
                    databasePool.free(connection);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"结束线程...");
            }, "t" + i).start();
        }
    }
}

final class DatabasePool {
    private volatile int poolsize;
    private Connection[] connections;
    private AtomicIntegerArray state;//0表示空闲 1 表示连接
    private volatile int BORDER;
    private final int KEEPLIVE_FACTOR=10;
    private final int DILATATION_FACTOR=8;
    private int waitsize=0;
    private final int MAX_WAIT_TIME;
    public DatabasePool(int poolsize) {
        //初始化连接池大小
        this.poolsize = poolsize;
        //初始化扩容边界
        BORDER=poolsize*DILATATION_FACTOR;
        //初始化连接
        connections = new Connection[poolsize];
        for (int i = 0; i < poolsize; i++) {
            connections[i] = new MockConnection("连接" + (i + 1));
        }
        //初始化连接状态
        state = new AtomicIntegerArray(poolsize);
        //初始化最大超时时间 可由用户指定
        MAX_WAIT_TIME=10;// s
    }

    public Connection borrow() {
        long begTime=System.currentTimeMillis();
        long passTime=0L;
        while (true) {
            if(passTime>=MAX_WAIT_TIME*1000)throw new RuntimeException("连接超时...");
            for (int i = 0; i < poolsize; i++) {
                if (state.get(i) == 0) {
                    if (state.compareAndSet(i, 0, 1)) {
                        System.out.println(Thread.currentThread().getName() + "获得连接..." + connections[i]);
                        return connections[i];
                    }
                }
            }
            synchronized (this) {//防止cpu一直空转
                try {
                    ++waitsize;
                    //判断是否需要扩容
                    if(waitsize>=BORDER){
                        dilatation();
                    }
                    System.out.println(Thread.currentThread().getName() + "等待连接...");
                    passTime=System.currentTimeMillis()-begTime;
                    this.wait(MAX_WAIT_TIME*1000-passTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    --waitsize;
                }
            }
            passTime=System.currentTimeMillis()-begTime;
        }
        //return borrow();//这样在大量连接的情况下可能发生 stack of overflow
    }
    //扩容  copyOnWrite
    private void dilatation() {
        System.out.println("扩容...");
        Connection[] connections = new Connection[BORDER];
        AtomicIntegerArray state = new AtomicIntegerArray(BORDER);
        //copy原数组
        for (int i = 0; i <poolsize ; i++) {
            connections[i]=this.connections[i];
            state.set(i,this.state.get(i));
        }
        for (int i = poolsize; i <BORDER ; i++) {
            connections[i]=new MockConnection("连接"+(i+1));
        }
        this.connections= connections;
        this.state=state;
        this.poolsize=BORDER;
        this.BORDER=poolsize*DILATATION_FACTOR;
    }
    public void free(Connection connection) {
        for (int i = 0; i < poolsize; i++) {
            if (connection == connections[i]) {
                state.set(i, 0);
                System.out.println(Thread.currentThread().getName() + "释放连接..." + connections[i]);
                synchronized (this) {
                    //保活 keeplive
                    if(waitsize<=0){
                       keeplive();
                    }
                    waitsize=0;
                    this.notifyAll();
                }
            }
        }
    }

    private void keeplive() {
        System.out.println("保活...");
        try {
            for (int j = 0; j <poolsize ; j++) {
                if(state.get(j)==0&&connections[j].isClosed()){
                    connections[j]=new MockConnection("连接"+(j=1));
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

class MockConnection implements Connection {
    private String name;

    @Override
    public String toString() {
        return "MockConnection{" +
                "name='" + name + '\'' +
                '}';
    }

    public MockConnection(String name) {
        this.name = name;
    }

    @Override
    public Statement createStatement() throws SQLException {
        return null;
    }

    @Override
    public PreparedStatement prepareStatement(String sql) throws SQLException {
        return null;
    }

    @Override
    public CallableStatement prepareCall(String sql) throws SQLException {
        return null;
    }

    @Override
    public String nativeSQL(String sql) throws SQLException {
        return null;
    }

    @Override
    public void setAutoCommit(boolean autoCommit) throws SQLException {

    }

    @Override
    public boolean getAutoCommit() throws SQLException {
        return false;
    }

    @Override
    public void commit() throws SQLException {

    }

    @Override
    public void rollback() throws SQLException {

    }

    @Override
    public void close() throws SQLException {

    }

    @Override
    public boolean isClosed() throws SQLException {
        return false;
    }

    @Override
    public DatabaseMetaData getMetaData() throws SQLException {
        return null;
    }

    @Override
    public void setReadOnly(boolean readOnly) throws SQLException {

    }

    @Override
    public boolean isReadOnly() throws SQLException {
        return false;
    }

    @Override
    public void setCatalog(String catalog) throws SQLException {

    }

    @Override
    public String getCatalog() throws SQLException {
        return null;
    }

    @Override
    public void setTransactionIsolation(int level) throws SQLException {

    }

    @Override
    public int getTransactionIsolation() throws SQLException {
        return 0;
    }

    @Override
    public SQLWarning getWarnings() throws SQLException {
        return null;
    }

    @Override
    public void clearWarnings() throws SQLException {

    }

    @Override
    public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
        return null;
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
        return null;
    }

    @Override
    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
        return null;
    }

    @Override
    public Map<String, Class<?>> getTypeMap() throws SQLException {
        return null;
    }

    @Override
    public void setTypeMap(Map<String, Class<?>> map) throws SQLException {

    }

    @Override
    public void setHoldability(int holdability) throws SQLException {

    }

    @Override
    public int getHoldability() throws SQLException {
        return 0;
    }

    @Override
    public Savepoint setSavepoint() throws SQLException {
        return null;
    }

    @Override
    public Savepoint setSavepoint(String name) throws SQLException {
        return null;
    }

    @Override
    public void rollback(Savepoint savepoint) throws SQLException {

    }

    @Override
    public void releaseSavepoint(Savepoint savepoint) throws SQLException {

    }

    @Override
    public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        return null;
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        return null;
    }

    @Override
    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        return null;
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
        return null;
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
        return null;
    }

    @Override
    public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
        return null;
    }

    @Override
    public Clob createClob() throws SQLException {
        return null;
    }

    @Override
    public Blob createBlob() throws SQLException {
        return null;
    }

    @Override
    public NClob createNClob() throws SQLException {
        return null;
    }

    @Override
    public SQLXML createSQLXML() throws SQLException {
        return null;
    }

    @Override
    public boolean isValid(int timeout) throws SQLException {
        return false;
    }

    @Override
    public void setClientInfo(String name, String value) throws SQLClientInfoException {

    }

    @Override
    public void setClientInfo(Properties properties) throws SQLClientInfoException {

    }

    @Override
    public String getClientInfo(String name) throws SQLException {
        return null;
    }

    @Override
    public Properties getClientInfo() throws SQLException {
        return null;
    }

    @Override
    public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
        return null;
    }

    @Override
    public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
        return null;
    }

    @Override
    public void setSchema(String schema) throws SQLException {

    }

    @Override
    public String getSchema() throws SQLException {
        return null;
    }

    @Override
    public void abort(Executor executor) throws SQLException {

    }

    @Override
    public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {

    }

    @Override
    public int getNetworkTimeout() throws SQLException {
        return 0;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }
}
